#include "test.h"

#define FIFO_BUFLEN 4

typedef struct {
    xthread_sema_t full;
    xthread_sema_t empty;
    xthread_mutex_t mutex;
    char *buf;
    int count;
    int size;
    char *head;
    char *tail;
} fifo_t;

fifo_t *fifo_create(int size)
{
    fifo_t *fifo = malloc(sizeof(fifo_t));
    if (!fifo)
        return NULL;
    fifo->buf = malloc(sizeof(char) * size);
    if (!fifo->buf) {
        free(fifo);
        return NULL;
    }
    fifo->size = size;
    fifo->count = 0;
    fifo->head = fifo->buf;
    fifo->tail = fifo->buf;
    xthread_sema_init(&fifo->empty, size);
    xthread_sema_init(&fifo->full, 0);
    xthread_mutex_init(&fifo->mutex);
    return fifo;
}

int fifo_destroy(fifo_t *fifo)
{
    if (!fifo)
        return -1;
    if (fifo->buf)
        free(fifo->buf);
    free(fifo);
    return 0;
}

int fifo_put(fifo_t *fifo, char ch)
{
    if (!fifo)
        return -1;
    xthread_sema_wait(&fifo->empty);
    xthread_mutex_lock(&fifo->mutex);

    *fifo->head++ = ch;
    if (fifo->head >= fifo->buf + fifo->size)
        fifo->head = fifo->buf;
    fifo->count++;
    xthread_mutex_unlock(&fifo->mutex);
    xthread_sema_post(&fifo->full);
    return 0;
}

int fifo_get(fifo_t *fifo, char *ch)
{
    if (!fifo)
        return -1;
    xthread_sema_wait(&fifo->full);
    xthread_mutex_lock(&fifo->mutex);

    *ch = *fifo->tail++;
    if (fifo->tail >= fifo->buf + fifo->size)
        fifo->tail = fifo->buf;
    fifo->count++;

    xthread_mutex_unlock(&fifo->mutex);
    xthread_sema_post(&fifo->empty);
    return 0;
}

fifo_t *chario;

static void *thread_writer(void *arg)
{
    char ch = 'a';
    while (1)
    {
        printf("writer %d:put char: %c\n", xthread_self(), ch);
        fifo_put(chario, ch++);
        if (ch > 'z')
            ch = 'a';
    }
}

static void *thread_reader(void *arg)
{
    char ch;
    while (1)
    {
        fifo_get(chario, &ch);
        printf("reader %d:get char: %c\n", xthread_self(), ch);
    }
}

int test_fifo(int argc, char *argv[])
{
    chario = fifo_create(FIFO_BUFLEN);
    if (!chario) {
        printf("create fifo failed!\n");
        return -1;
    }

    xthread_t tid0;
    if (xthread_create(&tid0, NULL, thread_writer, NULL) < 0)
            printf("xthread create failed!\n");
    xthread_t tid1;
    if (xthread_create(&tid1, NULL, thread_reader, NULL) < 0)
            printf("xthread create failed!\n");
    void *status;
    xthread_join(tid0, &status);
    printf("thread %x exit with %x\n", tid0, status);
    return 0;
}
